#include "TaskScheduler.h"

/***************************************************************************************
 * function name: JobLinkedList()
 * The Input: None
 * The output: None
 * The Function operation: Creates a new linked list holding job objects, using data
 * 						   linked list constructor
 ***************************************************************************************/
TaskScheduler::JobLinkedList::JobLinkedList(): DataLinkedList() { }

/***************************************************************************************
 * function name: virtual ~JobLinkedList()
 * The Input: None
 * The output: None
 * The Function operation: Deletes all nodes' pointed data
 ***************************************************************************************/
TaskScheduler::JobLinkedList::~JobLinkedList() {
	// use an iterator to scan list and delete each scanned node's pointed data
	DataLinkedList::Iterator itr = begin();
	while (*itr != NULL) { // scan to list's end
		delete ((Job*)*itr); // delete pointed data
		++itr; // move to next node
	}

}

void TaskScheduler::JobLinkedList::internalAdd(Job& newData, const Iterator& positionHolder) {
	DataLinkedList::internalAdd(newData, positionHolder);
}

/***************************************************************************************
 * function name: virtual void addNode(Data& newData)
 * The Input: Reference to data object to be added
 * The output: None
 * The Function operation: Adds given data object to the list
 ***************************************************************************************/
//void TaskScheduler::addJob(Job& newJob) {
/***************************************************************************************
* function name: void addJob(const unsigned int startTime, const unsigned int length, const int priority)
* The Input: New job's attributes
* The output: None
* The Function operation: Creates a new job with given attributes and adds it to job list
***************************************************************************************/
void TaskScheduler::addJob(const unsigned int startTime, const unsigned int length, const int priority) {

	// create job according to inheriting scheduler
	Job& newJob = createJob(startTime, length, priority);

	// use an iterator to find new job's position in list
	DataLinkedList::Iterator jobListItr = m_listByTime.begin();
	++jobListItr; // move to first valid node on list
	while (*jobListItr != NULL) {

		if ( newJob.isEarly( *(Job*)(*jobListItr) ) )  { // found position
			m_listByTime.internalAdd(newJob, jobListItr); // add to by-time-list
			return;
		}
		++jobListItr; // move to next node
	}
	m_listByTime.internalAdd(newJob, jobListItr); // add as last node
	return;
}

/***************************************************************************************
* function name: bool runsBefore(const Job& firstJobOperand, const Job& secondJobOperand) const
* The Input: Reference to job to be compared with
* The output: True if operating job object should run before given job, False if not
* The Function operation: // TODO
***************************************************************************************/
bool TaskScheduler::runsBefore(const Job& firstJobOperand, const Job& secondJobOperand) const {

	// if given job's priority is lower, operating job object runs first
	if (firstJobOperand.isImprotant(secondJobOperand)) {
		return true;
	}
	// if given job's priority is higher, given job runs first
	else if (secondJobOperand.isImprotant(firstJobOperand)) {
		return false;
	}

	// if reached here, than jobs' priorities are equal

//	// the job whose last execution time is earlier should run first // TODO DELETE
//	if (firstJobOperand.executedEarlier(secondJobOperand)) {
//		return true; // operating job object runs first
//	}
//	else if (secondJobOperand.executedEarlier(firstJobOperand)) {
//		return false; // given job runs first
//	}

	// if reached here, that jobs' start time is equal

	// the job that was created earlier runs first, hence compare job ID's
	if (firstJobOperand.isNewer(secondJobOperand)) {
		return true; // operating job runs first
	}
	return false; // given job's ID is smaller, therefore runs first
}

void TaskScheduler::addRoundRobin(Job& newJob) { // TODO DELETE!!!

	// use an iterator to find new job's position in list
	DataLinkedList::Iterator jobListItr = m_jobsToRunList.begin(); // get an iterator for list's start
	++jobListItr; // move to first valid node on list

	while (*jobListItr != NULL) {

		if (runsBefore(newJob, *(Job*)(*jobListItr) )) { // found position
			m_jobsToRunList.internalAdd(newJob, jobListItr); // add new node
			return;
		}
		++jobListItr; // move to next node
	}
	m_jobsToRunList.internalAdd(newJob, jobListItr); // add as last node

}

/***************************************************************************************
* function name: TaskScheduler()
* The Input: None
* The output: None
* The Function operation: Creates a new task scheduler holding job objects
***************************************************************************************/
TaskScheduler::TaskScheduler(): m_listByTime( *(new JobLinkedList()) ), m_jobsToRunList( *(new JobLinkedList()) ) { }

/***************************************************************************************
* function name: ~TaskScheduler()
* The Input: None
* The output: None
* The Function operation: Deletes both pointed job linked lists
***************************************************************************************/
TaskScheduler::~TaskScheduler() {
	// delete pointed job lists
	delete &m_listByTime;
	delete &m_jobsToRunList;
}

void TaskScheduler::addRoundRobin(DataLinkedList::Iterator& readyJobsItr, Job& currJobCopy) {
	// initialize an iterator to jobs to run list's start
	bool foundPosition = false;
	readyJobsItr = m_jobsToRunList.begin(); // iterator to ready-list's start
	++readyJobsItr; // move to first valid node

	while (*readyJobsItr != NULL) { // scan list to its end
		if (runsBefore(currJobCopy, *(Job*) (*readyJobsItr))) { // found current job copy's position in jobs to run list
			m_jobsToRunList.internalAdd(currJobCopy, readyJobsItr); // add copy
			foundPosition = true;
			break;
		}
		++readyJobsItr; // move to next node
	}
	if (foundPosition == false) {
		m_jobsToRunList.internalAdd(currJobCopy, readyJobsItr); // add copy to list's end
	}
}

/***************************************************************************************
 * function name: void work(const int systemTime, const int type)
 * The Input: Current system time and job's type
 * The output: None
 * The Function operation: Executes first job on priority queue
 ***************************************************************************************/
/*void*/ bool TaskScheduler::work(const int systemTime, const int type) {

	DataLinkedList::Iterator listByTimeItr = m_listByTime.begin(); // iterator to by-time-list's start
	++listByTimeItr; // move to first valid node

	DataLinkedList::Iterator readyJobsItr = m_jobsToRunList.begin(); // iterator to ready-list's start
	++readyJobsItr; // move to first valid node

//	// TODO DEBUG PRINT!!!
//	cout << "in TaskScheduler::work" << endl;
//	cout << "-------------------------------------------------------" << endl;
//	cout << "print all jobs sorted by time" << endl;
//	m_listByTime.print(systemTime, type);
//	cout << "done" << endl;
//	cout << "starts taking out jobs and adding them to ready-list" << endl;

	// take out all jobs that are ready to run
	while (*listByTimeItr != NULL) { // scan to list's end
		//if ( ((Job*)(*listByTimeItr))->isEarly(systemTime) ) { // current job is ready to run // TODO DEBUG!!!
		if ( ((Job*)(*listByTimeItr))->isEarly(systemTime) || ((Job*)(*listByTimeItr))->isStartTimeEqual(systemTime)) { // current job is ready to run
			Job& currJobCopy = duplicateJob(*(Job*) ((*listByTimeItr))); // copy current job (using right job type's constructor)
			addRoundRobin(readyJobsItr, currJobCopy); // add to ready-list
			m_listByTime.removeNode(listByTimeItr); // delete current job from by-time-list

//			// TODO DEBUG PRINT!!!
//			cout << "print by-time list after removal" << endl;
//			m_listByTime.print(systemTime,type);
//			cout << "done" << endl;
		}
		++listByTimeItr; // move to next node on by-time-list
	}

	if (executeJob(systemTime, type)) { // try executing first job on ready-list
		sortRoundRobin(); // sort ready-list after execution
		return true;
	}

	return false; // no job was executed

//	bool isSuccess = executeJob(systemTime, type); // execute first job on ready-list
//	if (isSuccess) {
//		sortRoundRobin(); // sort ready-list after execution
//		return isSuccess; // worked successfully
//	}
//	return isSuccess; // return

}

void TaskScheduler::sortRoundRobin() {

//	// TODO DEBUG PRINT!!!
//	cout << "in TaskScheduler::sortRoundRobin" << endl;
//	cout << "-------------------------------------------------------" << endl;

	// after execution, sort ready to run job list
	DataLinkedList::Iterator readyJobsItr = ((DataLinkedList&)m_jobsToRunList).begin();
	DataLinkedList::Iterator prevNodeOnList = readyJobsItr; // initialize second iterator to list's start

	// move to first iterator to second valid node on list
	++readyJobsItr;
	++readyJobsItr;
	// move second iterator to first valid node on list
	++prevNodeOnList;

	// move executed job to the end of group of job with its priority
	while ( *readyJobsItr != NULL) {
		//if ( runsBefore( *(Job*)(*readyJobsItr), *(Job*)(*prevNodeOnList) ) ) { // TODO DEBUG!!!
		if ( ((Job*)(*readyJobsItr))->isPriorityEqual( *(Job*)(*prevNodeOnList) ) ) { // node hold jobs of same priority
			// swap nodes
			m_jobsToRunList.swapNodes(readyJobsItr, prevNodeOnList);
		}
		++readyJobsItr; // TODO DEBUG!!!
		++prevNodeOnList; // TODO DEBUG!!!
	}

//	// TODO DEBUG PRINT!!!
//	cout << "print ready jobs list after sorting" << endl;
//	m_jobsToRunList.print(20,2);
//	cout << "done" << endl;
}

/*void*/ bool TaskScheduler::executeJob(const int systemTime, const int type) {

//	// TODO DEBUG PRINT!!!
//	cout << "in TaskScheduler::executeJob" << endl;
//	cout << "-------------------------------------------------------" << endl;

	// execute first job on ready to run job list
	DataLinkedList::Iterator readyJobsItr = ((DataLinkedList&)m_jobsToRunList).begin(); // get first job on list
	++readyJobsItr; // move to first valid job

	if (*readyJobsItr != NULL) { // check if there is a job to run

//		// TODO DEBUG PRINT!!!
//		cout << "executing job" << endl;
//		////////////////

		((Job*)(*readyJobsItr))->work(systemTime, type); // execute first job

//		// TODO DEBUG PRINT!!!
//		cout << "done" << endl;
//		////////////////

		// if executed job is done, remove it from list
		if ( ((Job*)(*readyJobsItr))->isJobDone() ) {

//			// TODO DEBUG PRINT!!!
//			cout << "print ready jobs list BEFORE removal" << endl;
//			m_jobsToRunList.print(systemTime,type);
//			cout << "done" << endl;

			m_jobsToRunList.removeNode(readyJobsItr); // remove node from ready-list

//			// TODO DEBUG PRINT!!!
//			cout << "print ready jobs list AFTER removal" << endl;
//			m_jobsToRunList.print(systemTime,type);
//			cout << "done" << endl;
		}
		return true; // job was executed
	}
	return false; // no job to run, return False
}

bool TaskScheduler::isEmpty() const {
	// check if there are no jobs waiting to be executed
	bool noWatingJobs = m_listByTime.isEmpty();
	// check if ready-to-run job list is empty
	bool noReadyJobs = m_jobsToRunList.isEmpty();
	// scheduler is empty if there are neither jobs waiting to be executed nor jobs ready to be executed
	return (noWatingJobs && noReadyJobs);
}

/////////////////////////////////////////////////////////////////////// TODO DEBUG!!!
void TaskScheduler::JobLinkedList::print(const int time, const int type) {

	DataLinkedList::Iterator itr = ((DataLinkedList&)(*this)).begin();
	++itr;
	cout << "printing list:" << endl;
	while (*itr != NULL) {
		((Job*)(*itr))->work(time, type);
		cout << endl;
		++itr;
	}
}
